package com.jshan.blog.aspect;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
import com.jshan.blog.annotation.OperationLog;
import com.jshan.blog.base.admin.entity.SysOperationLog;
import com.jshan.blog.base.admin.entity.SysUser;
import com.jshan.blog.constans.QueueConst;
import com.jshan.blog.constans.AuthUserInfoConst;
import com.jshan.blog.utils.RedisUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.Arrays;

@Aspect
@Component
@Slf4j
public class OperationLogAspect {

    @Resource
    private RedisUtil redisUtil;

    // 拦截所有标记了 @OperationLog的方法
    @Around("@annotation(operationLog)")
    public Object logAround(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
        // 1. 获取请求上下文
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 2. 记录开始时间
        Long startTime = System.currentTimeMillis();
        // 3. 初始化日志对象
        SysOperationLog logInfo = new SysOperationLog();

        try {
            // 4. 设置基础信息
            logInfo.setOperationTime(LocalDateTime.now());
            logInfo.setClientIp(getClientIp(request));
            logInfo.setRequestMethod(request.getMethod());
            logInfo.setClientType(request.getHeader("User-Agent"));
            // 5. 从注解中获取操作描述和类型
            logInfo.setOperationDesc(operationLog.value());
            logInfo.setOperationType(operationLog.operationType());

            // 6. 从 Redis 获取用户信息
            String value = redisUtil.getValue(AuthUserInfoConst.USER_INFO_KEY + StpUtil.getLoginIdAsInt());
            SysUser userInfo = JSON.parseObject(value, new TypeReference<SysUser>() {
            });
            // 7. 从登陆体系获取用户名
            if (ObjectUtils.isNotEmpty(userInfo)) {
                logInfo.setUsername(userInfo.getUsername());
            }
            // 8. 获取请求参数（json序列化）
            // 修改后：
            Object[] filteredArgs = Arrays.stream(joinPoint.getArgs())
                    .filter(arg -> !(arg instanceof MultipartFile)) // 过滤掉 MultipartFile
                    .toArray();
            logInfo.setRequestParams(JSON.toJSONString(filteredArgs));

            // 9. 执行目标方法并记录结果
            Object result = joinPoint.proceed();
            // 创建过滤器，排除 "data" 字段
            SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
            filter.getExcludes().add("data"); // 排除不需要的字段
            String filteredJson = JSON.toJSONString(result, filter); // 应用过滤器

            logInfo.setResponseResult(filteredJson); // 保存过滤后的结果
            logInfo.setStatusCode(HttpServletResponse.SC_OK);
            logInfo.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            // 打印日志信息
            // log.info("日志信息：{}", logInfo);
            // 7. 执行目标方法，获取返回值
            return result;
        } catch (Exception e) {
            // 10. 记录异常信息
            logInfo.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            logInfo.setErrorMessage(e.getMessage());
            log.error("操作日志记录失败", e);  // 记录异常堆栈信息
            throw e;  // 重新抛出异常
        } finally {
            // 11. 记录操作耗时
            Long endTime = System.currentTimeMillis();
            logInfo.setCostTime(endTime - startTime);
            // 你可以选择将日志信息保存到数据库或者日志文件等
            // 保存日志的方法可以在此处调用
            // 12. 异步保存日志
            // log.info("操作日志：{}", logInfo);
            redisUtil.lPush(QueueConst.LOG_QUEUE_KEY, JSON.toJSONString(logInfo));
        }
    }
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
